package com.yanqu.road.dao.impl.player;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.yanqu.road.dao.db.DbNameStrategy;
import com.yanqu.road.dao.db.DbParameter;
import com.yanqu.road.dao.impl.TempAutoDataObjectDao;
import com.yanqu.road.entity.autodata.AutoUserDataClazzInfo;
import com.yanqu.road.entity.autodata.AutoDataObject;
import com.yanqu.road.entity.autodata.SqlTypeHelper;
import com.yanqu.road.utils.classutil.ClassHelper;

import java.lang.invoke.MethodHandle;
import java.lang.reflect.*;
import java.sql.CallableStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class UserCommonActivityDataDaoImpl<T extends AutoDataObject> extends TempAutoDataObjectDao<T> {

    public T getUserActivityData(Class<T> clazz, int activityId, long userId) {
        ResultSet rs = null;
        CallableStatement call = null;
        try {
            AutoUserDataClazzInfo autoUserDataClazzInfo = AutoDataObject.getAutoDataFieldInfo(clazz);
            if (autoUserDataClazzInfo == null) {
                return null;
            }
            String tableName = autoUserDataClazzInfo.getTableName();
            String sql = "select * from " + tableName + " where activityId=? and userId=?";
            getDb().setConnName(DbNameStrategy.getUserDb());
            call = getDb().getConnection().prepareCall(sql);
            int i = 1;
            call.setInt(i++, activityId);
            call.setLong(i++, userId);
            rs = call.executeQuery();
            while (rs.next()) {
                return parse(rs, clazz);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            getDb().closeConnection(call, rs);
        }
        return null;
    }

    public Map<Long, T> getActivityUserData(Class<T> clazz, int activityId) {
        ResultSet rs = null;
        CallableStatement call = null;
        Map<Long, T> res = new ConcurrentHashMap<>();
        try {
            AutoUserDataClazzInfo autoUserDataClazzInfo = AutoDataObject.getAutoDataFieldInfo(clazz);
            if (autoUserDataClazzInfo == null) {
                return null;
            }
            String tableName = autoUserDataClazzInfo.getTableName();
            String sql = "select * from " + tableName + " where activityId=?";
            getDb().setConnName(DbNameStrategy.getUserDb());
            call = getDb().getConnection().prepareCall(sql);
            int i = 1;
            call.setInt(i++, activityId);
            rs = call.executeQuery();
            while (rs.next()) {
                long userId = rs.getLong("userId");
                if (!res.containsKey(userId)) {
                    res.put(userId, parseData(rs, clazz));
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            getDb().closeConnection(call, rs);
        }
        return res;
    }

    public T getUserActivityData(Class<T> clazz, long userId) {
        ResultSet rs = null;
        CallableStatement call = null;
        try {
            AutoUserDataClazzInfo autoUserDataClazzInfo = AutoDataObject.getAutoDataFieldInfo(clazz);
            if (autoUserDataClazzInfo == null) {
                return null;
            }
            String tableName = autoUserDataClazzInfo.getTableName();
            String sql = "select * from " + tableName + " where userId=?";
            getDb().setConnName(DbNameStrategy.getUserDb());
            call = getDb().getConnection().prepareCall(sql);
            int i = 1;
            call.setLong(i++, userId);
            rs = call.executeQuery();
            while (rs.next()) {
                return parse(rs, clazz);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            getDb().closeConnection(call, rs);
        }
        return null;
    }

    public List<T> getUserDataList(Class<T> clazz, long userId) {
        List<T> tempList = new ArrayList<>();
        ResultSet rs = null;
        CallableStatement call = null;
        try {
            AutoUserDataClazzInfo autoUserDataClazzInfo = AutoDataObject.getAutoDataFieldInfo(clazz);
            if (autoUserDataClazzInfo == null) {
                return null;
            }
            String tableName = autoUserDataClazzInfo.getTableName();
            String sql = "select * from " + tableName + " where userId=?";
            getDb().setConnName(DbNameStrategy.getUserDb());
            call = getDb().getConnection().prepareCall(sql);
            int i = 1;
            call.setLong(i++, userId);
            rs = call.executeQuery();
            while (rs.next()) {
                tempList.add(parse(rs, clazz));
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            getDb().closeConnection(call, rs);
        }
        return tempList;
    }

    @Override
    protected boolean updateData(T data) {
        Class<? extends AutoDataObject> clazz = data.getClass();
        AutoUserDataClazzInfo autoUserDataClazzInfo = AutoDataObject.getAutoDataFieldInfo(clazz);
        if (autoUserDataClazzInfo == null) {
            return false;
        }
        try {
            int i = 1;
            Map<Integer, DbParameter> para = new HashMap<Integer, DbParameter>();
            List<Field> keyList = autoUserDataClazzInfo.getKeyList();
            // 设置字段值
            List<Field> fields = autoUserDataClazzInfo.getFields();
            for (Field field : fields) {
                if (keyList.contains(field)) {
                    continue;
                }
                MethodHandle getter = autoUserDataClazzInfo.findGetterHandle(field);
                MethodHandle setter = autoUserDataClazzInfo.findSetterHandle(field);

                if (getter != null && setter != null) {
                    Object value = getter.invoke(data);
                    if (value != null) {
                        Integer sqlType = SqlTypeHelper.getSqlType(field.getType());
                        if (sqlType != null) {
                            para.put(i++, new DbParameter(sqlType, value));
                        } else {
                            para.put(i++, new DbParameter(Types.VARCHAR, JSON.toJSONString(value)));
                        }
                    }
                }
            }

            for (Field field : keyList) {
                MethodHandle getter = autoUserDataClazzInfo.findGetterHandle(field);
                Object value = getter.invoke(data);
                Integer sqlType = SqlTypeHelper.getSqlType(field.getType());
                if (sqlType != null) {
                    para.put(i++, new DbParameter(sqlType, value));
                } else {
                    para.put(i++, new DbParameter(Types.VARCHAR, JSON.toJSONString(value)));
                }
            }

            data.setBeginWriteTime();
            getDb().setConnName(DbNameStrategy.getUserDb());

            if (getDb().executeNoneQuery(data.getUpdateSql(), para) > 0) {
                return true;
            }
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }

        return false;
    }

    @Override
    protected boolean addData(T data) {
        Class<? extends AutoDataObject> clazz = data.getClass();
        AutoUserDataClazzInfo autoUserDataClazzInfo = AutoDataObject.getAutoDataFieldInfo(clazz);
        if (autoUserDataClazzInfo == null) {
            return false;
        }
        data.setBeginWriteTime();
        getDb().setConnName(DbNameStrategy.getUserDb());
        int i = 1;
        Map<Integer, DbParameter> para = new HashMap<Integer, DbParameter>();
        try {
            List<Field> fields = autoUserDataClazzInfo.getFields();
            for (Field field : fields) {

                MethodHandle getter = autoUserDataClazzInfo.findGetterHandle(field);
                Object value = getter.invoke(data);
                if (value != null) {
                    Integer sqlType = SqlTypeHelper.getSqlType(field.getType());
                    if (sqlType != null) {
                        para.put(i++, new DbParameter(sqlType, value));
                    } else {
                        para.put(i++, new DbParameter(Types.VARCHAR, JSON.toJSONString(value)));
                    }
                } else {
                    para.put(i++, new DbParameter(Types.VARCHAR, "null"));
                }
            }
        } catch (Throwable e) {
            logger.error("SQL Error", e);
            throw new RuntimeException(e);
        }

        if (getDb().executeNoneQuery(data.getInsertSql(), para) > 0) {
            return true;
        }
        return false;

    }

    @Override
    protected T parseData(ResultSet rs, Class<T> clazz) throws SQLException {
        AutoUserDataClazzInfo autoUserDataClazzInfo = AutoDataObject.getAutoDataFieldInfo(clazz);
        if (autoUserDataClazzInfo == null) {
            return null;
        }
        T instance = null;
        try {
            Constructor<?> constructor = autoUserDataClazzInfo.getConstructor();
            instance = (T) constructor.newInstance();
            // 设置字段值
            List<Field> fields = autoUserDataClazzInfo.getFields();
            for (Field field : fields) {
                MethodHandle getter = autoUserDataClazzInfo.findGetterHandle(field);
                MethodHandle setter = autoUserDataClazzInfo.findSetterHandle(field);

                if (getter != null && setter != null) {
                    Object value = rs.getObject(field.getName());
                    if (value != null) {
                        Integer sqlType = SqlTypeHelper.getSqlType(field.getType());
                        if (sqlType == null) {
                            invokeJsonStr(instance, field, setter, value);
                        }else if (sqlType == Types.BOOLEAN) {
                            setter.invoke(instance, rs.getBoolean(field.getName()));
                        }else {
                            setter.invoke(instance, value);
                        }
                    }
                }
            }
        } catch (Throwable e) {
            logger.error("SQL Error", e);
            throw new RuntimeException(e);
        }

        return instance;
    }

    private void invokeJsonStr(T instance, Field field, MethodHandle setter, Object value) throws Throwable {
        if (field.getType().isAssignableFrom(Set.class)) {//list
            //得到Set里的类型
            Class<?> genericType = ClassHelper.getClassFromType(field.getGenericType(), 0);
            List<?> objects = JSON.parseArray(value.toString(), genericType);
            Set<?> set = new HashSet<Object>(objects);
            setter.invoke(instance, set);
        } else if (field.getType().isAssignableFrom(List.class)) {//list
            //得到List里的类型
            Class<?> genericType = ClassHelper.getClassFromType(field.getGenericType(), 0);
            setter.invoke(instance, JSON.parseArray(value.toString(), genericType));
        } else if (field.getType().isAssignableFrom(LinkedList.class)) {//list
            //得到List里的类型
            Class<?> genericType = ClassHelper.getClassFromType(field.getGenericType(), 0);
            List<?> objects = JSON.parseArray(value.toString(), genericType);
            setter.invoke(instance, new LinkedList<Object>(objects));
        } else if (field.getType().isAssignableFrom(Map.class)) {//map
            //得到Map的key类型和value类型
            Class<?> keyType = ClassHelper.getClassFromType(field.getGenericType(), 0);
            Class<?> valueType = ClassHelper.getClassFromType(field.getGenericType(), 1);
            Map<Object, Object> objectObjectMap = JSONObject.parseObject(value.toString(), new TypeReference<Map<Object, Object>>(keyType, valueType) {
            });
            Map<Object, Object> tempMap = null;
            if (field.getType().isAssignableFrom(ConcurrentHashMap.class)) {
                tempMap = new ConcurrentHashMap<>();
            } else {
                tempMap = new HashMap<>();
            }
            for (Map.Entry<Object, Object> entry : objectObjectMap.entrySet()) {
                Object key = JSONObject.parseObject(entry.getKey().toString(), keyType);
                Object mapValue = JSONObject.parseObject(entry.getValue().toString(), valueType);
                tempMap.put(key, mapValue);
            }
            setter.invoke(instance, tempMap);
        } else {
            setter.invoke(instance, JSON.parseObject(value.toString(), field.getType()));
        }
    }

    public static void main(String[] args) {
        try {
            Package pack = AutoDataObject.class.getPackage();
            for (Class<?> clazz : ClassHelper.getClasses(pack)) {
                if (!AutoDataObject.class.isAssignableFrom(clazz)) {
                    continue;
                }

                if (!clazz.getSimpleName().contains("ChuHan")) {
                    continue;
                }

                String s = AutoDataObject.parseCreateTableSql(clazz);
                System.out.println(s);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void save(AutoDataObject data) {
        if (data.isInsertOption()) {
            new UserCommonActivityDataDaoImpl().add(data);
        } else if (data.isUpdateOption()) {
            new UserCommonActivityDataDaoImpl().update(data);
        }
    }
}
